package com.yukefms.rule;

import com.yukefms.common.ListTools;
import com.yukefms.engine.running.MatchedStatus;
import com.yukefms.engine.running.RunningContext;
import com.yukefms.jointpoint.BodyPointCloud;
import com.yukefms.rule.formula.SeqStageBAFormula;
import com.yukefms.rule.formula.SimpleStageBAFormula;
import com.yukefms.rule.formula.interfaces.DynamicBAFormula;
import com.yukefms.rule.formula.interfaces.StageBAFormula;
import com.yukefms.rule.formula.interfaces.StaticBAFormula;
import com.yukefms.rule.interfaces.BAStageRule;
import com.yukefms.rule.interfaces.RuleBuilder;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description the TriggerStageBARule is specifying following action pattern:\
 * Trigger  -->  Stage   -->  Invariant
 *
 * ---------
 * Trigger:    Invoke rule
 * Stage :     Response/target needs to finish through dynamic action
 * Invariant:  Need to keep it satisfiable during performing.
 * ----------
 *
 *
 * @Author Ping
 * @Date 2021/6/16 1:01
 **/
public class TriggerStageBARule extends AbstractBARule {
    protected StaticBAFormula trigger ;
    /**
     * note that, the stage might contain stageList to finish.
     */
    protected StageBAFormula stage ;
    protected StageBAFormula currentStage ;
    protected StaticBAFormula invariant ;
    protected List<String> relatedJointType ;

    public TriggerStageBARule(final String ruleName, final StaticBAFormula trigger, final DynamicBAFormula dynamic,
                              final StaticBAFormula response, final StaticBAFormula invariant) {
        super(ruleName);
        this.trigger = trigger;
        this.stage = new SimpleStageBAFormula(dynamic,response) ;
        this.invariant = invariant;
        this.currentStage = stage ;
    }


    public TriggerStageBARule(String ruleName, StaticBAFormula trigger, StageBAFormula stage, StaticBAFormula invariant) {
        super(ruleName);
        this.trigger = trigger;
        this.stage = stage;
        this.invariant = invariant;
        this.currentStage = stage.currentStage() ;
    }

    @Override
    public StaticBAFormula getTrigger() {
        return this.trigger;
    }

    @Override
    public StaticBAFormula getResponse() {
        return this.stage.response();
    }

    @Override
    public StaticBAFormula getInvariant() {
        return this.invariant;
    }

    @Override
    public DynamicBAFormula getDynamic() {
        return this.stage.dynamic();
    }

    @Override
    public List<String> getInvolvedJointTypes() {
        if (this.relatedJointType != null) {
            return this.relatedJointType ;
        }
        this.relatedJointType = this.trigger.relatedJointTypes() ;
        ListTools.addAllNoDumplicate( this.relatedJointType, this.stage.relatedJointTypes()); ;
        ListTools.addAllNoDumplicate( this.relatedJointType, this.invariant.relatedJointTypes()) ;
        return  this.relatedJointType;
    }

    @Override
    public StageBAFormula currentStage() {
        return stage.currentStage();
    }

    @Override
    public boolean hasNextStage() {
        return this.stage.hasNextStage();
    }

    @Override
    public void toNextStage() {
        this.currentStage = this.stage.nextStage();
    }

    @Override
    public BAStageRule addMoreStage(StageBAFormula formula) {
        this.stage.add(formula);
        return this ;
    }


    @Override
    public boolean responseArrived(BodyPointCloud pointCloud, RunningContext context) {
        if (this.currentStage == null) {
            return true ;
        }
        return this.currentStage.response().matched(pointCloud, context) == MatchedStatus.TRUE ;
    }

    @Override
    public boolean isPerforming(BodyPointCloud pointCloud, RunningContext context) {
        return currentStage.dynamic().matched(pointCloud, context) == MatchedStatus.TRUE ;
    }

    @Override
    public void redo() {
        this.stage.reset() ;
        this.currentStage = stage.currentStage() ;
    }

    public static TriggerStageRuleBuilder builder() {
        return new TriggerStageRuleBuilder() ;
    }
    public static class TriggerStageRuleBuilder implements RuleBuilder {
        StaticBAFormula trigger ;
        StageBAFormula stage ;
        StaticBAFormula invariant ;
        String ruleName ;

        public TriggerStageRuleBuilder setTrigger(StaticBAFormula trigger) {
            this.trigger = trigger ;
            return this ;
        }
        public TriggerStageRuleBuilder setStage(StageBAFormula stage) {
            this.stage = stage ;
            return this ;
        }
        public TriggerStageRuleBuilder setInvariant(StaticBAFormula invariant) {
            this.invariant = invariant;
            return this ;
        }

        public TriggerStageRuleBuilder setRuleName(String ruleName) {
            this.ruleName = ruleName ;
            return this ;
        }
         @Override
        public TriggerStageBARule build() {
            if (this.trigger == null) {
                throw new IllegalArgumentException("The trigger formula is null.") ;
            }
            if (this.invariant == null) {
                throw new IllegalArgumentException("The invariant formula is null.") ;
            }
            if (this.stage == null) {
                throw new IllegalArgumentException("The stage formula is null.") ;
            }
            if (this.ruleName == null) {
                throw new IllegalArgumentException("The ruleName of formula is null.") ;
            }
            return new TriggerStageBARule(this.ruleName, this.trigger, this.stage, this.invariant) ;
        }

    }

}
